package com.syh.core.ddd.infrastructure.repository;

import cn.redscarf.commons.util.bean.ExtBeanUtils;
import cn.redscarf.commons.wrapper.Wrapper;
import com.syh.core.ddd.domain.Aggregate;
import com.syh.core.expction.UnexpectedlyException;
import com.syh.jn.scm.infrastructure.wrapper.ResolveToHQL;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.hibernate.Query;
import org.springframework.orm.hibernate5.HibernateTemplate;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class AggregateRepoHibernateImplTmpl<Entity extends Aggregate<ID>, Model, ID extends Serializable> {

  private HibernateTemplate hibernateTemplate;

  private Class<ID> idClass;
  private Class<Model> modelClass;
  private Class<Entity> entityClass;


  private AggregateRepoHibernateImplTmpl(Builder builder) {
    this.hibernateTemplate = builder.hibernateTemplate;
    this.idClass = builder.idClass;
    this.modelClass = builder.modelClass;
    this.entityClass = builder.entityClass;
  }

  public Model add(Entity entity) {
    //将领域对象数据复制到数据模型中
    try {
      Model model = modelClass.newInstance();
      ExtBeanUtils.copyProperties(entity, model);
      this.hibernateTemplate.save(model);
      return model;
    } catch (Exception e) {
      throw new UnexpectedlyException(e);
    }
  }

  public void update(Entity entity) {
    try {
      Model model = (Model) this.hibernateTemplate.get(modelClass, entity.getId());
      if (model != null) {
        ExtBeanUtils.copyProperties(entity, model);
      } else {
        model = modelClass.newInstance();
        ExtBeanUtils.copyProperties(entity, model);
      }
      this.hibernateTemplate.update(model);
    } catch (Exception e) {
      throw new UnexpectedlyException(e);
    }
  }

  public Entity getById(ID id) {
    Model model = (Model) this.hibernateTemplate.get(modelClass, id);
    if (model == null) {
      return null;
    }
    return this.transform(model);
  }

  public void removeById(ID id) {
    Model model = (Model) this.hibernateTemplate.get(this.modelClass, id);
    if (model != null) {
      this.hibernateTemplate.delete(model);
    }
  }

  public Entity getOne(Wrapper wrapper) {
    List<Entity> entityList = this.superFindListPage(wrapper, 1, 1);
    return entityList.isEmpty() ? null : entityList.get(0);
  }

  public List<Entity> getList(Wrapper wrapper) {
    return this.superFindListPage(wrapper, 0, Integer.MAX_VALUE);
  }

  public List<Entity> findListPage(Wrapper wrapper, Integer startRow, Integer count) {
    return this.superFindListPage(wrapper, startRow, count);
  }

  public int getCount(Wrapper wrapper) {
    ResolveToHQL parse = new ResolveToHQL(wrapper);
    StringBuffer hql = new StringBuffer();
    hql.append("SELECT COUNT(*) FROM ");
    hql.append(modelClass.getSimpleName());
    hql.append(" ");
    hql.append(" WHERE 1=1 ");
    hql.append(parse.resolve());
    Query query = this.buildQuery(hql);
    return ((Long) query.uniqueResult()).intValue();
  }

  public boolean contains(ID id) {
    return this.getById(id) == null;
  }

  public void remove(Entity entity) {
    this.removeById(entity.getId());
  }


  /**
   * 业务模型转领域模型
   *
   * @param modelList
   * @return
   */
  private List<Entity> transform(List<Model> modelList) {
    List<Entity> voList = modelList.stream().map((model) -> {
      return this.transform(model);
    }).collect(Collectors.toList());
    return voList;
  }

  /**
   * 业务模型转领域模型
   *
   * @return
   */
  private Entity transform(Model model) {
    Entity entity = null;
    try {
      Constructor constructors = this.entityClass.getDeclaredConstructor();
      constructors.setAccessible(true);
      entity = (Entity) constructors.newInstance();
      ExtBeanUtils.copyProperties(model, entity);
    } catch (Exception e) {
      throw new UnexpectedlyException("初始化 entity 异常");
    }
    return entity;
  }

  /**
   * 建立hql query对象
   *
   * @param hql
   * @return
   */
  public Query buildQuery(StringBuffer hql) {
    return this.buildQuery(hql.toString());
  }

  /**
   * 建立hql query对象
   *
   * @param hql
   * @return
   */
  public Query buildQuery(String hql) {
    return this.hibernateTemplate.getSessionFactory().getCurrentSession().createQuery(hql);
  }

  /**
   * 内部方法，所有列表查询都调用本方法 改方法是Hibernate实现 注意增删改 业务必须调用该接口数据 不能用view后缀的数据
   *
   * @param wrapper
   * @param startRow
   * @param count
   * @return
   */
  private List<Entity> superFindListPage(Wrapper wrapper, Integer startRow, Integer count) {
    ResolveToHQL parse = new ResolveToHQL(wrapper);
    StringBuffer hql = new StringBuffer();
    hql.append("FROM ");
    hql.append(modelClass.getSimpleName());
    hql.append(" ");
    hql.append(" WHERE 1=1 ");
    hql.append(parse.resolve());
    Query query = this.hibernateTemplate.getSessionFactory().getCurrentSession().createQuery(hql.toString());
    if (startRow != null && count != null) {
      query.setFirstResult((startRow - 1) * count);
      query.setMaxResults(count);
    }

    List<Model> modelList = query.list();
    if (modelList.isEmpty()) {
      return Collections.emptyList();
    }
    return this.transform(modelList);
  }


  public static final class Builder<ID, Model, Entity> {
    private HibernateTemplate hibernateTemplate;
    private Class<ID> idClass;
    private Class<Model> modelClass;
    private Class<Entity> entityClass;

    public Builder() {

    }

    public Builder hibernateTemplate(HibernateTemplate val) {
      hibernateTemplate = val;
      return this;
    }

    public Builder idClass(Class<ID> val) {
      idClass = val;
      return this;
    }

    public Builder modelClass(Class<Model> val) {
      modelClass = val;
      return this;
    }

    public Builder entityClass(Class<Entity> val) {
      entityClass = val;
      return this;
    }

    public AggregateRepoHibernateImplTmpl build() {
      return new AggregateRepoHibernateImplTmpl(this);
    }
  }
}
